/**
 * @file: test.cpp
 * A mixed set of unit tests for all data structures, present in the repo.
 * Based on Boost Test Library
 */
#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>

/**
 * stack testing
 */
#include "stack.hpp"

BOOST_AUTO_TEST_CASE( stackTest )
{
    Stack<int> a;
    BOOST_CHECK_THROW( a.pop(), ExceptionEmpty); 
    a.push(1);
    BOOST_CHECK_THROW( {a.pop(); a.pop();}, ExceptionEmpty);
    a.push(2);
    BOOST_CHECK_EQUAL( a.pop(), 2);
    BOOST_CHECK_EQUAL( a.empty(), true);
    a.push(3);
    BOOST_CHECK_EQUAL( a.empty(), false);
    a.push(4);
    BOOST_CHECK_EQUAL( a.length(), 2);
    std::stringstream dyn1, dyn2;
    dyn1 << a;
    BOOST_CHECK_EQUAL( dyn1.str(), "4 3 \n");
    a.pop();
    a.pop();
    dyn2 << a;
    BOOST_CHECK_EQUAL( dyn2.str(), "Stack is empty. \n"); 
/** 
 * This cases were tested:
   1) Using 'pop()' when stack is empty. Exception throws.
   2) Push()'ing something in stack and then call pop() twice. Exception throws.
      (So we can see, that after calling pop() the element deletes from stack)
   3) Push() variable then call pop(). 
      (We see that the variable we get after calling pop() is variable that we've push()'ed)
   4) We make sure that empty() function works. 
   5) We make sure that length() function works. 
   6) operator<< works. (Including the case when stack is empty)
 */  
}

/**
 * associative_array testing
 */
#include "associative_array.hpp"

BOOST_AUTO_TEST_CASE( assocArrayTest )
{
    Array<int, char> a;
    char key = 'k';
    int value = 100;
    BOOST_CHECK_EQUAL( a.check_key( key ), true);
    BOOST_CHECK_EQUAL( a.remove( key ), false);
    BOOST_CHECK_THROW( a.find( key ), ExceptionFind);
    BOOST_CHECK_EQUAL( a.insert( key, value ), true);
    BOOST_CHECK_EQUAL( a.insert( key, value ), false);
    BOOST_CHECK_EQUAL( a.check_key( key ), false);
    BOOST_CHECK_EQUAL( a.find( key ), 100);
    const Array<int, char> b = a;
    BOOST_CHECK_EQUAL( a.remove( key ), true);
    BOOST_CHECK_EQUAL( a.check_key( key ), true);
    BOOST_CHECK_EQUAL( b.find( key ), 100);
    char key1 = 's';
    BOOST_CHECK_THROW( b.find( key1 ), ExceptionConstFind);

/**
 * This cases were tested:
 * 1) Using 'check_key( key )' when there isnt item with key. 
 * 2) Using 'remove( key )' when there isnt removing item.
 * 3) Using 'find( key)' when there isnt item with key. Exeption throws
 * 4) Using legal 'insert( key, value ) 
 * 5) Using 'insert( key, value ) when item with the same key have existed yet
 * 6) Using 'check_key( key )' when item with key exists
 * 7) Using 'find( key )' when key exist
 * 8) Remove our item
 * 9) Make sure that we remove item
 */
    //BOOST_FAIL( "Not implemented!");
}

/**
 * bintree testing
 */
#include "bintree.hpp"
BOOST_AUTO_TEST_CASE( bintreeTest )
{
    BSTree<double> tree;
    std::stringstream s1;
    BOOST_CHECK_EQUAL( tree.find( 7), 0);
    s1 << tree;
    BOOST_CHECK_EQUAL( s1.str(), "Tree is empty!\n");
    std::stringstream s2;
    tree.infixTraverce( s2);
    BOOST_CHECK_EQUAL( s2.str(), "Tree is empty!\n");
    tree.addNode( 5.5);
    tree.addNode( -1);
    tree.addNode( -100);
    tree.addNode( 1);
    tree.addNode( 13);
    std::stringstream s3;
    s3 << tree;
    BOOST_CHECK_EQUAL( s3.str(), "            -100\n      -1\n            1\n5.5\n      13\n");
    std::stringstream s4;
    tree.infixTraverce( s4);
    BOOST_CHECK_EQUAL( s4.str(), "-100\n-1\n1\n5.5\n13\n");
    std::stringstream s5;
    s5 << tree.find( 1) << "\n";
    s5 << tree.find( -10.3) << "\n";
    BOOST_CHECK_EQUAL( s5.str(), "1\n0\n");
    tree.addNode( 16);
}
/**
 * bitset testing
 */
#include "bitset.hpp"
BOOST_AUTO_TEST_CASE( bitsetTest )
{
    BitSet a( 19);
    a.setBit( 0, true);
    a.setBit( 1, false);
    a.setBit( 2, true);
    a.setBit( 3, false);
    a.setBit( 4, false);
    a.setBit( 5, true);
    a.setBit( 6, false); 
    a.setBit( 7, true);
    a.setBit( 8, false);
    a.setBit( 9, false);
    a.setBit( 10, true);
    a.setBit( 11, false);
    a.setBit( 12, true);
    a.setBit( 13, false);
    a.setBit( 14, false);
    a.setBit( 15, true);
    a.setBit( 16, false);
    a.setBit( 17, false);
    a.setBit( 18, true);

    std::stringstream strbuf, str2;
    strbuf << a;
    BOOST_CHECK_EQUAL( strbuf.str(), "1010010100101001001");
    BOOST_CHECK_THROW( a.getBit( 19), OutOfRangeException);
    BOOST_CHECK_THROW( a.setBit( 20, false), OutOfRangeException);
    OutOfRangeException bitsetexc( 0, 1);
    
    str2 << bitsetexc.data();
    BOOST_CHECK_EQUAL( str2.str(), "ERROR: No 0 element in bitset! Max is 0!\n");
}

/**
 * queue testing
 */
#include "queue.hpp"
BOOST_AUTO_TEST_CASE( queueTest )
{
    /* Newly created queue */
    Queue <int> qu;
    BOOST_CHECK_EQUAL( qu.size(), 0);
    BOOST_CHECK_EQUAL( qu.empty(), true);
    /* Queue with float */
    Queue<float> q1;
    q1.push( 2.4);
    q1.push( 13.5);
    q1.push( 82.5);
    BOOST_CHECK_EQUAL( q1.size(), 3);
    BOOST_CHECK_EQUAL( q1.empty(), false);
    BOOST_CHECK_CLOSE( q1.front(), 2.4, 0.00001);
    BOOST_CHECK_CLOSE( q1.back(), 82.5, 0.00001);
    q1.pop();
    BOOST_CHECK_CLOSE( q1.front(), 13.5, 0.00001);
    q1.clear();
    BOOST_CHECK_EQUAL( q1.size(), 0);
    BOOST_CHECK_EQUAL( q1.empty(), true);
    /* Queue with string */
    Queue<std::string> q2;
    q2.push( "first string");
    BOOST_CHECK_EQUAL( q2.empty(), false);
    BOOST_CHECK_EQUAL( q2.back(), "first string");
    q2.push( "second string");
    BOOST_CHECK_EQUAL( q2.back(), "second string");
    q2.pop();
    BOOST_CHECK_EQUAL( q2.front(), "second string");
    /* Operator "=" */
    Queue<int> q3;
    q3.push( 2);
    q3.push( 4);
    BOOST_CHECK_EQUAL( q3.empty(), 0);
    Queue<int> q4;
    q4 = q3;
    BOOST_CHECK_EQUAL( q4.front(), 2);
    q4.pop();
    BOOST_CHECK_EQUAL( q4.front(), 4);
    /* Construction "queue = queue" */
    Queue<int> q7;
    q7.push(5);
    q7 = q7;
    BOOST_CHECK_EQUAL( q7.empty(), false);
    BOOST_CHECK_EQUAL( q7.size(), 1);
    BOOST_CHECK_EQUAL( q7.front(), 5);
    /** 
     * This cases were tested:
     * 1) Using push() with different queues (different types).
     * 2) Using size() (the number of elements).
     * 3) Using empty() (=0 if the queue isn't empty).
     * 4) Check the operator=.
     * 5) Check function empty(), size(), clear() for newly created queue.
     */
}

/**
 * priority_queue testing
 */
#include "priority_queue.hpp"
BOOST_AUTO_TEST_CASE( pqueueTest )
{
    PQueue<int> q;
    int a = -1;
    BOOST_CHECK_EQUAL( q.serialize(), "\0");
    BOOST_CHECK_EQUAL( q.maxPriority( &a), false);
    q.add( 0, 1);
    BOOST_CHECK_EQUAL( q.serialize(), "0 ");
    q.add( 1, 3);
    BOOST_CHECK_EQUAL( q.serialize(), "1 0 ");
    q.add( 4, 2);
    BOOST_CHECK_EQUAL( q.serialize(), "1 0 4 ");
    BOOST_CHECK_EQUAL( q.maxPriority( &a), true);
    BOOST_CHECK_EQUAL( a, 1);
    q.add( 3, 4);
    q.add( 5, 10);
    BOOST_CHECK_EQUAL( q.serialize(), "5 3 4 0 1 ");
    q.maxPriority( &a);
    BOOST_CHECK_EQUAL( a, 5);
    q.deleteMax();
    BOOST_CHECK_EQUAL( q.serialize(), "3 1 4 0 ");
    q.maxPriority( &a);
    BOOST_CHECK_EQUAL( a, 3);
    q.add( 10, -1);
    BOOST_CHECK_EQUAL( q.serialize(), "3 1 4 0 10 ");
}

